<?php

namespace Gql\Core;

use Gql\ScalarType\MixedType;
use GraphQL\Language\Parser;
use GraphQL\Utils\AST;
use Symfony\Component\Filesystem\Filesystem;

class Parse
{
    public $filesystem;
    public $entryFile;
    public $cacheFile;
    public $fragments = [];
    public $schema = '';

    public function __construct(Filesystem $filesystem)
    {
        $this->filesystem = $filesystem;
    }

    public function setEntryFile(string $entryFile)
    {
        $this->entryFile = $entryFile;
    }

    public function setCacheFile(string $cacheFile)
    {
        $this->cacheFile = $cacheFile;
    }

    public function getDocument(bool $debug = false)
    {
        if ($debug) {
            $document = null;
            $cacheFile = $this->cacheFile;
            if (!is_file($cacheFile)) {
                $document = $this->parseDocument();
                $contents = "<?php\nreturn " . var_export(AST::toArray($document), true) . ';';
                $dirName=dirname($cacheFile);
                if(!is_dir($dirName)){
                    $this->filesystem->mkdir($dirName);
                }
                file_put_contents($cacheFile, $contents);
            } else {
                $document = AST::fromArray(require_once $cacheFile);
            }
        } else {
            $document = $this->parseDocument();
        }
        return $document;
    }

    protected function parseDocument()
    {
        return Parser::parse($this->getSchema());
    }

    /**
     * 返回合法的 sdl
     * @return string
     */
    public function getSchema(): string
    {
        if (empty($this->schema)) {
            $schema = $this->parseTypes($this->entryFile);
            $schema = preg_replace_callback(
                "/([\"']{3}[^{}]*)*fragment\s+(\w+)\s*{([^{}]*)}/",
                [$this, 'parseFragment'],
                $schema
            );
            $schema = preg_replace_callback(
                "/\.{3}\s*(\w+)/",
                [$this, 'replaceSpread'],
                $schema
            );
            $this->schema = $schema;
        }
        return $this->schema;
    }

    /**
     * 解析替换掉所有的 fragment
     * @param array $matches
     * @return string
     */
    protected function parseFragment(array $matches): string
    {
        if (!empty($matches[2])) {
            $this->fragments[$matches[2]] = $matches[3];
        }
        return '';
    }

    /**
     * 使用 fragment 替换掉所有的 扩展操作符语法
     * @param array $matches
     * @return string
     */
    protected function replaceSpread(array $matches): string
    {
        $fragments = $this->fragments;
        if (isset($fragments[$matches[1]])) {
            return $fragments[$matches[1]];
        }
        return '';
    }

    /**
     * 解析导入所有的SDL语言
     * @param string $entryPath 入口文件
     * @return string
     */
    protected function parseTypes(string $entryPath): string
    {
        $content = '';
        if (is_file($entryPath)) {
            $dirName = dirname($entryPath) . '/';
            $content .= file_get_contents($entryPath) . PHP_EOL; //
            $res = preg_match_all("/\s*#\s*require\s*([_\\\.\w\d*\/]+)/", $content, $matches);
            if ($res) {
                foreach ($matches[1] as $v) {
                    $files = glob($dirName . $v);
                    foreach ($files as $file) {
                        $content .= $this->parseTypes($file);
                    }
                }
            }
        }
        return $content;
    }
}